﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using DeepTalkDBServiceTest.DTDBService;
using DeepTalkDBServiceTest.ServiceReference1;
using System.ServiceModel;
using System.Threading;
using System.Diagnostics;

namespace DeepTalkDBServiceTest
{
    /// <summary>
    /// Test the update time stuff
    /// </summary>
    [TestClass]
    public class t_RenderTalksWithUpdateTimes
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestInitialize]
        public void InitTest()
        {
            Utils.StartMainService();
            FileInfo db = new FileInfo("ConfData_" + TestContext.TestName + ".sdf");
            File.Copy(@"..\..\..\DeepTalkDBService\ConferenceData.sdf", db.FullName);
            using (var rc = new DeepTalkDBServiceTest.DTDBService.DTDBClient())
            {
                rc.SetDBFile(db);
            }
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Utils.ShutdownMainService();
        }

        class abortRenderer : IDTRenderCallback
        {
            public DTRenderClient RC { get; set; }

            public TimeSpan DeltaToUpdate { get; set; }

            public bool FinishTheRender { get; set; }

            public abortRenderer()
            {
                FinishTheRender = false;
                DeltaToUpdate = new TimeSpan(0);
            }

            AutoResetEvent evt = new AutoResetEvent(false);
            public bool RenderTalk(DTRenderTalkInfo info)
            {
                Trace.WriteLine("Got callback in abortRender");
                ThreadPool.QueueUserWorkItem(o =>
                {
                    RC.NextUpdateTime(info.ID, DateTime.Now + DeltaToUpdate);
                    if (FinishTheRender)
                    {
                        RC.RenderDone(info.ID, (float)10.0, 100);
                    }
                    evt.Set();
                });
                return true;
            }

            internal bool WaitForIt(TimeSpan timeSpan)
            {
                return evt.WaitOne((int)timeSpan.TotalMilliseconds);
            }
        }

        class waitForRender : IDTRenderCallback
        {
            AutoResetEvent evt = new AutoResetEvent(false);

            public int ID { get; set; }

            public bool RenderTalk(DTRenderTalkInfo info)
            {
                Trace.WriteLine("Got Render call back in waitForRender");
                ID = info.ID;
                evt.Set();
                return true;
            }

            internal bool WaitForIt(TimeSpan timeSpan)
            {
                return evt.WaitOne((int) timeSpan.TotalMilliseconds);
            }
        }


        /// <summary>
        /// Render a talk, set the update to be a second in the future, and then "crash" and make sure the talk comes up again.
        /// </summary>
        [TestMethod]
        public void TestUpdateWithNoFinishUpdateTimeInPast()
        {
            TestUpdateWithNoFinishWithParams(TimeSpan.FromSeconds(-1), false);
        }

        [TestMethod]
        public void TestUpdateWithNoFinishUpdateTimeInFuture()
        {
            TestUpdateWithNoFinishWithParams(TimeSpan.FromSeconds(2), false);
        }

        /// <summary>
        /// Run the update and reset, with some changes to make it easy to do a parameterized study
        /// </summary>
        /// <param name="delta"></param>
        private static void TestUpdateWithNoFinishWithParams(TimeSpan delta, bool finishTheRender)
        {
            /// 1. Submit a new talk.

            using (var rc = new DTDBClient())
            {
                rc.SubmitNewTalk(Utils.GetTalkXML(), "talk 1", DateTime.Now);
                Assert.AreEqual(1, rc.GetUnrenderedTalks(10).Length, "Expected just 1 unrendered talk");
            }

            /// Render it and "forget about it", but set the update time for 1 second from now.

            var ar = new abortRenderer();
            ar.DeltaToUpdate = delta;
            ar.FinishTheRender = finishTheRender;
            using (var r = new DTRenderClient(new InstanceContext(ar)))
            {
                ar.RC = r;
                r.ReadyForWork();
                Assert.IsTrue(ar.WaitForIt(TimeSpan.FromSeconds(5)), "Expecting a first call back to do the scan");
                using (var rc = new DTDBClient())
                {
                    Assert.AreEqual(finishTheRender ? 0 : 1, rc.GetUnrenderedTalks(10).Length, "Expected just 1 unrendered talk still");
                }
            }

            /// See see if we can get another one to show up.

            var wr = new waitForRender();
            using (var rr = new DTRenderClient(new InstanceContext(wr)))
            {
                rr.ReadyForWork();
                Assert.IsTrue(wr.WaitForIt(TimeSpan.FromSeconds(5)), "Expecting a first call back to do the second scan");
                rr.RenderDone(wr.ID, (float)10.0, 10);
            }

            /// Make sure there are no unrendered talks in there!

            using (var rc = new DTDBClient())
            {
                Assert.AreEqual(1, rc.GetRenderedTalks(10).Length, "Expected just 1 rendered talk!");
            }
        }

        [TestMethod]
        public void TestUpdateWithFinish()
        {
            TestUpdateWithNoFinishWithParams(TimeSpan.FromSeconds(2), true);
        }
    }
}
